---@class RedRob.CameraController
local CameraController = RedRob.CreateSingleton("CameraController")
local this = CameraController
function CameraController.OnCreate()
    ---@type RedRob.Player
    this.actor = nil
    ---@type Camera
    this.camera = nil
    ---@type Transform
    this.cameraTran = nil
    this.cameraPos = nil
    this.isPosDirty = false
    this.bottomLeftPos = nil
    this.topRightPos = nil
    RedRob.ClientBattle.RegisterLateUpdateCallback(this.LateUpdate)
end
function CameraController.OnDestroy()
    this.actor = nil
    this.camera = nil
    this.cameraTran = nil
    this.cameraPos = nil
    this.isPosDirty = false
end
function CameraController.SetCamera(camera)
    this.camera = camera
    if camera then
        this.cameraTran = camera.transform
        this.cameraPos = this.cameraTran.localPosition
    else
        this.cameraTran = nil
    end
end
function CameraController.CameraActive(isActive)
    if this.camera then
        this.camera.gameObject:SetActive(isActive)
    end
end
---World pos -> Screen pos
---@return number,number,number
function CameraController.WorldToScreenPoint(x,y,z)
    local x,y,z = Util.WorldToScreenPoint(this.camera,x,y,z,0,0,0)
    return x,y,z
end
---@return number,number,number
function CameraController.ScreenToWorldPoint(x,y,z)
    local x,y,z = Util.ScreenToWorldPoint(this.camera,x,y,z,0,0,0)
    return x,y,z
end
---@param player RedRob.Player
function CameraController.IsPlayerInScreen(player)
    if not player then return end
    return this.IsPosInScreen(player:GetPos().x,2)
end
---@param player RedRob.Actor
function CameraController.IsPosInScreen(x,radius)
    if this.bottomLeftPos == nil then return true end
    local minX = this.cameraPos.x + this.bottomLeftPos.x
    local maxX = this.cameraPos.x + this.topRightPos.x
    if x + radius < minX or x - radius > maxX then
        return false
    end
    return true
end
function CameraController.SetFollowTarget(actor)
    this.actor = actor
    this.isPosDirty = true
end
function CameraController.NotifyActorPosChange(actor)
    if this.actor == actor then
        this.isPosDirty = true
    end
end
---@return UnityEngine.RaycastHit
function CameraController.RayCast(pos)
    if not pos then
        pos = Input.mousePosition
    end
    local ray = this.camera:ScreenPointToRay(pos)
    local flag,hit = UnityEngine.Physics.Raycast(ray,nil,300)
    if flag then
        return hit
    end
    return nil
end

---@return UnityEngine.RaycastHit[]
function CameraController.RayCastAll(pos)
    local flag = false
    if not pos then
        pos = Vector3.GetCacheVec():Set(GetMousePosition())
        flag = true
    end
    local ray = this.camera:ScreenPointToRay(pos)
    local hits = UnityEngine.Physics.RaycastAll(ray,300)
    if flag then
        Vector3.ReturnToPool(pos)
    end
    return hits
end
function CameraController.SetCameraPos(x,y)
    local battle = RedRob.ClientBattle
    if not this.bottomLeftPos then
        this.bottomLeftPos = Vector3(battle.WorldToLocal(this.ScreenToWorldPoint(0,0,0)))
        this.topRightPos = Vector3(battle.WorldToLocal(this.ScreenToWorldPoint(Screen.width,Screen.height,0)))
    end
    this.cameraPos.x = x
    this.cameraPos.y = y
    if this.cameraPos.x + this.bottomLeftPos.x <= battle.GetMinX() then
        this.cameraPos.x = battle.GetMinX() - this.bottomLeftPos.x
    end
    if this.cameraPos.x + this.topRightPos.x >= battle.GetMaxX() then
        this.cameraPos.x = battle.GetMaxX() - this.topRightPos.x
    end

    if this.cameraPos.y + this.topRightPos.y >= battle.GetMaxY() then
        this.cameraPos.y = battle.GetMaxY() - this.topRightPos.y
    end
    if this.cameraPos.y + this.bottomLeftPos.y <= battle.GetMinY() then
        this.cameraPos.y = battle.GetMinY() - this.bottomLeftPos.y
    end

    this.isPosDirty = false
    Util.Lua_SetLocalPositionXYZ(this.cameraTran,this.cameraPos.x,this.cameraPos.y,this.cameraPos.z)
end
function CameraController.LateUpdate(dt)
    if not this.actor or not this.cameraTran then return end
    if not this.isPosDirty then
        return
    end
    FireEvent(RedRob.Config.Event.CameraPosChanged)
    local pos = this.actor:GetPos()
    this.SetCameraPos(pos.x,pos.y)
end
